package cn.com.yuuuu.config;

import cn.com.yuuuu.domain.model.ConfigI18n;
import cn.com.yuuuu.holder.ContextHolder;
import cn.com.yuuuu.mapper.ConfigI18nMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class MyMessageSource extends AbstractMessageSource implements ResourceLoaderAware {
    // 这个是用来缓存数据库中获取到的配置的 数据库配置更改的时候可以调用reload方法重新加载
    private static final Map<String, Map<String, String>> LOCAL_CACHE = new ConcurrentHashMap<>(256);
    private final Logger logger = LoggerFactory.getLogger(MyMessageSource.class);
    ResourceLoader resourceLoader;
    @Resource
    private ConfigI18nMapper configI18nMapper;

    /**
     * 初始化
     * Java中该注解的说明：@PostConstruct该注解被用来修饰一个非静态的void（）方法。
     * 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行，并且只会被服务器执行一次。
     * PostConstruct在构造函数之后执行，init（）方法之前执行。
     */
    @PostConstruct
    public void init() {
        this.reload();
    }

    /**
     * 重新将数据库中的国际化配置加载
     */
    public void reload() {
        LOCAL_CACHE.clear();
        LOCAL_CACHE.putAll(loadAllMessageResourcesFromDB());
    }

    /**
     * 从数据库中获取所有国际化配置 这边可以根据自己数据库表结构进行相应的业务实现
     * 对应的语言能够取出来对应的值就行了 无需一定要按照这个方法来
     */
    public Map<String, Map<String, String>> loadAllMessageResourcesFromDB() {
        // 获取数据库配置
        List<ConfigI18n> list = configI18nMapper.selectList(new LambdaQueryWrapper<>());
        if (!list.isEmpty()) {
            final Map<String, String> zhCnMessageResources = new HashMap<>(list.size());
            final Map<String, String> enUsMessageResources = new HashMap<>(list.size());
            final Map<String, String> myMessageResources = new HashMap<>(list.size());
            for (ConfigI18n item : list) {
                // 根据不同语言，分配到对应语言的值中
                if (item.getLang().equals("zh")) {
                    zhCnMessageResources.put(item.getKey1(), item.getValue1());
                } else if (item.getLang().equals("en")) {
                    enUsMessageResources.put(item.getKey1(), item.getValue1());
                } else if (item.getLang().equals("my")) {
                    myMessageResources.put(item.getKey1(), item.getValue1());
                }
            }

            // 加入缓存
            LOCAL_CACHE.put("zh", zhCnMessageResources);
            LOCAL_CACHE.put("en", enUsMessageResources);
            LOCAL_CACHE.put("my", myMessageResources);
        }
        return new HashMap<>();
    }

    /**
     * 从缓存中取出国际化配置对应的数据 或者从父级获取
     *
     * @param code
     * @param locale 可以为null, 表示从当前HttpServletRequest中获取语言
     * @return
     */
    public String getSourceFromCache(String code, Locale locale) {


        String language = ContextHolder.get("lang");
        // 获取缓存中对应语言的所有数据项
        Map<String, String> props = LOCAL_CACHE.get(language);
        if (null != props && props.containsKey(code)) {
            // 如果对应语言中能匹配到数据项，那么直接返回
            return props.get(code);
        } else {
            // 如果对应语言中不能匹配到数据项，从上级获取返回
            try {
                if (null != this.getParentMessageSource()) {
                    return this.getParentMessageSource().getMessage(code, null, locale);
                }
            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
            // 如果上级也没有找到，那么返回请求键值
            return code;
        }
    }

    // 下面三个重写的方法是比较重要的
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    protected MessageFormat resolveCode(String code, Locale locale) {
        String msg = getSourceFromCache(code, locale);
        return new MessageFormat(msg, locale);
    }

    @Override
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        return getSourceFromCache(code, locale);
    }

}